package com.rickpan.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户在线状态服务
 * 基于Redis实现用户在线状态管理
 * 
 * @author RickPan Team
 * @version 1.0.0
 */
@Service
public class UserOnlineService {

    private static final Logger logger = LoggerFactory.getLogger(UserOnlineService.class);

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    // Redis Key 前缀
    private static final String ONLINE_USERS_KEY = "online_users";
    private static final String USER_SESSIONS_PREFIX = "user_sessions:";
    private static final String SESSION_INFO_PREFIX = "session_info:";
    private static final String USER_HEARTBEAT_PREFIX = "user_heartbeat:";
    private static final String USER_STATUS_PREFIX = "user_status:";

    // 超时时间配置
    private static final long HEARTBEAT_TIMEOUT = 1800; // 30分钟心跳超时（临时延长用于测试）
    private static final long SESSION_TIMEOUT = 1800; // 30分钟会话超时

    /**
     * 用户上线
     * 
     * @param userId    用户ID
     * @param sessionId 会话ID
     * @param clientInfo 客户端信息
     */
    public void userOnline(Long userId, String sessionId, Map<String, Object> clientInfo) {
        try {
            String now = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
            
            // 1. 添加到在线用户集合
            redisTemplate.opsForHash().put(ONLINE_USERS_KEY, userId.toString(), now);
            
            // 2. 添加会话到用户会话集合
            String userSessionsKey = USER_SESSIONS_PREFIX + userId;
            redisTemplate.opsForSet().add(userSessionsKey, sessionId);
            redisTemplate.expire(userSessionsKey, SESSION_TIMEOUT, TimeUnit.SECONDS);
            
            // 3. 存储会话详细信息
            String sessionInfoKey = SESSION_INFO_PREFIX + sessionId;
            Map<String, Object> sessionInfo = new HashMap<>();
            sessionInfo.put("userId", userId);
            sessionInfo.put("sessionId", sessionId);
            sessionInfo.put("loginTime", now);
            sessionInfo.put("lastActiveTime", now);
            sessionInfo.put("clientInfo", clientInfo);
            
            redisTemplate.opsForHash().putAll(sessionInfoKey, sessionInfo);
            redisTemplate.expire(sessionInfoKey, SESSION_TIMEOUT, TimeUnit.SECONDS);
            
            // 4. 设置用户心跳
            updateUserHeartbeat(userId);
            
            // 5. 设置用户状态为在线
            setUserStatus(userId, "ONLINE");
            
            // logger.debug("用户上线: userId={}, sessionId={}", userId, sessionId); // 开发调试用
            
        } catch (Exception e) {
            logger.error("❌ 用户上线失败: userId={}, sessionId={}", userId, sessionId, e);
        }
    }

    /**
     * 用户下线
     * 
     * @param userId    用户ID
     * @param sessionId 会话ID
     */
    public void userOffline(Long userId, String sessionId) {
        try {
            // 1. 从用户会话集合中移除会话
            String userSessionsKey = USER_SESSIONS_PREFIX + userId;
            redisTemplate.opsForSet().remove(userSessionsKey, sessionId);
            
            // 2. 删除会话信息
            String sessionInfoKey = SESSION_INFO_PREFIX + sessionId;
            redisTemplate.delete(sessionInfoKey);
            
            // 3. 检查用户是否还有其他活跃会话
            Set<Object> remainingSessions = redisTemplate.opsForSet().members(userSessionsKey);
            if (remainingSessions == null || remainingSessions.isEmpty()) {
                // 没有其他会话，用户完全下线
                redisTemplate.opsForHash().delete(ONLINE_USERS_KEY, userId.toString());
                redisTemplate.delete(USER_HEARTBEAT_PREFIX + userId);
                setUserStatus(userId, "OFFLINE");
                
                logger.info("🔌 用户完全下线: userId={}", userId);
            } else {
                logger.info("🔌 用户会话断开: userId={}, sessionId={}, 剩余会话数: {}", 
                    userId, sessionId, remainingSessions.size());
            }
            
        } catch (Exception e) {
            logger.error("❌ 用户下线失败: userId={}, sessionId={}", userId, sessionId, e);
        }
    }

    /**
     * 更新用户心跳
     * 
     * @param userId 用户ID
     */
    public void updateUserHeartbeat(Long userId) {
        try {
            String now = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
            String heartbeatKey = USER_HEARTBEAT_PREFIX + userId;

            // 分别处理两个Redis操作，提供更详细的错误信息
            try {
                redisTemplate.opsForValue().set(heartbeatKey, now, HEARTBEAT_TIMEOUT, TimeUnit.SECONDS);
            } catch (Exception e) {
                logger.error("❌ 设置用户心跳失败: userId={}, key={}", userId, heartbeatKey, e);
                throw e;
            }

            try {
                // 更新在线用户列表中的最后活跃时间
                redisTemplate.opsForHash().put(ONLINE_USERS_KEY, userId.toString(), now);
            } catch (Exception e) {
                logger.error("❌ 更新在线用户列表失败: userId={}, key={}", userId, ONLINE_USERS_KEY, e);
                throw e;
            }

        } catch (Exception e) {
            logger.error("❌ 更新用户心跳失败: userId={}", userId, e);
        }
    }

    /**
     * 检查用户是否在线
     * 
     * @param userId 用户ID
     * @return 是否在线
     */
    public boolean isUserOnline(Long userId) {
        try {
            // 检查用户是否在在线列表中
            Boolean exists = redisTemplate.opsForHash().hasKey(ONLINE_USERS_KEY, userId.toString());
            if (!Boolean.TRUE.equals(exists)) {
                return false;
            }
            
            // 检查心跳是否超时
            String heartbeatKey = USER_HEARTBEAT_PREFIX + userId;
            Boolean heartbeatExists = redisTemplate.hasKey(heartbeatKey);
            
            return Boolean.TRUE.equals(heartbeatExists);
            
        } catch (Exception e) {
            logger.error("❌ 检查用户在线状态失败: userId={}", userId, e);
            return false;
        }
    }

    /**
     * 获取在线用户列表
     * 
     * @return 在线用户信息列表
     */
    public List<Map<String, Object>> getOnlineUsers() {
        try {
            Map<Object, Object> onlineUsersMap = redisTemplate.opsForHash().entries(ONLINE_USERS_KEY);
            
            return onlineUsersMap.entrySet().stream()
                .map(entry -> {
                    Map<String, Object> userInfo = new HashMap<>();
                    userInfo.put("userId", Long.parseLong(entry.getKey().toString()));
                    userInfo.put("lastActiveTime", entry.getValue().toString());
                    userInfo.put("isOnline", isUserOnline(Long.parseLong(entry.getKey().toString())));
                    return userInfo;
                })
                .filter(userInfo -> (Boolean) userInfo.get("isOnline"))
                .collect(Collectors.toList());
                
        } catch (Exception e) {
            logger.error("❌ 获取在线用户列表失败", e);
            return new ArrayList<>();
        }
    }

    /**
     * 获取在线用户数量
     * 
     * @return 在线用户数量
     */
    public long getOnlineUserCount() {
        try {
            return getOnlineUsers().size();
        } catch (Exception e) {
            logger.error("❌ 获取在线用户数量失败", e);
            return 0;
        }
    }

    /**
     * 获取用户的所有会话
     * 
     * @param userId 用户ID
     * @return 用户会话列表
     */
    public List<Map<String, Object>> getUserSessions(Long userId) {
        try {
            String userSessionsKey = USER_SESSIONS_PREFIX + userId;
            Set<Object> sessionIds = redisTemplate.opsForSet().members(userSessionsKey);
            
            if (sessionIds == null || sessionIds.isEmpty()) {
                return new ArrayList<>();
            }
            
            return sessionIds.stream()
                .map(sessionId -> {
                    String sessionInfoKey = SESSION_INFO_PREFIX + sessionId;
                    Map<Object, Object> sessionInfo = redisTemplate.opsForHash().entries(sessionInfoKey);
                    
                    Map<String, Object> result = new HashMap<>();
                    sessionInfo.forEach((key, value) -> result.put(key.toString(), value));
                    return result;
                })
                .filter(sessionInfo -> !sessionInfo.isEmpty())
                .collect(Collectors.toList());
                
        } catch (Exception e) {
            logger.error("❌ 获取用户会话失败: userId={}", userId, e);
            return new ArrayList<>();
        }
    }

    /**
     * 强制用户下线（踢出用户）
     * 
     * @param userId 用户ID
     */
    public void forceUserOffline(Long userId) {
        try {
            // 获取用户所有会话
            List<Map<String, Object>> sessions = getUserSessions(userId);
            
            // 逐个清理会话
            for (Map<String, Object> session : sessions) {
                String sessionId = (String) session.get("sessionId");
                if (sessionId != null) {
                    userOffline(userId, sessionId);
                }
            }
            
            // 清理用户相关的所有Redis数据
            redisTemplate.opsForHash().delete(ONLINE_USERS_KEY, userId.toString());
            redisTemplate.delete(USER_SESSIONS_PREFIX + userId);
            redisTemplate.delete(USER_HEARTBEAT_PREFIX + userId);
            setUserStatus(userId, "OFFLINE");
            
            logger.info("🚫 强制用户下线: userId={}", userId);
            
        } catch (Exception e) {
            logger.error("❌ 强制用户下线失败: userId={}", userId, e);
        }
    }

    /**
     * 清理超时的在线用户
     */
    public void cleanupTimeoutUsers() {
        try {
            Map<Object, Object> onlineUsersMap = redisTemplate.opsForHash().entries(ONLINE_USERS_KEY);
            
            for (Map.Entry<Object, Object> entry : onlineUsersMap.entrySet()) {
                Long userId = Long.parseLong(entry.getKey().toString());
                
                // 检查心跳是否超时
                if (!isUserOnline(userId)) {
                    forceUserOffline(userId);
                    logger.info("🧹 清理超时用户: userId={}", userId);
                }
            }
            
        } catch (Exception e) {
            logger.error("❌ 清理超时用户失败", e);
        }
    }

    /**
     * 设置用户状态
     * 
     * @param userId 用户ID
     * @param status 状态 (ONLINE/OFFLINE/AWAY)
     */
    private void setUserStatus(Long userId, String status) {
        try {
            String statusKey = USER_STATUS_PREFIX + userId;
            Map<String, Object> statusInfo = new HashMap<>();
            statusInfo.put("status", status);
            statusInfo.put("updateTime", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
            
            redisTemplate.opsForHash().putAll(statusKey, statusInfo);
            
            if ("OFFLINE".equals(status)) {
                redisTemplate.expire(statusKey, 3600, TimeUnit.SECONDS); // 离线状态保留1小时
            }
            
        } catch (Exception e) {
            logger.error("❌ 设置用户状态失败: userId={}, status={}", userId, status, e);
        }
    }

    /**
     * 获取用户状态
     * 
     * @param userId 用户ID
     * @return 用户状态信息
     */
    public Map<String, Object> getUserStatus(Long userId) {
        try {
            String statusKey = USER_STATUS_PREFIX + userId;
            Map<Object, Object> statusInfo = redisTemplate.opsForHash().entries(statusKey);
            
            Map<String, Object> result = new HashMap<>();
            statusInfo.forEach((key, value) -> result.put(key.toString(), value));
            
            // 如果没有状态信息，默认为离线
            if (result.isEmpty()) {
                result.put("status", "OFFLINE");
                result.put("updateTime", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
            }
            
            return result;
            
        } catch (Exception e) {
            logger.error("❌ 获取用户状态失败: userId={}", userId, e);
            Map<String, Object> defaultStatus = new HashMap<>();
            defaultStatus.put("status", "OFFLINE");
            defaultStatus.put("updateTime", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
            return defaultStatus;
        }
    }

    /**
     * 获取团队成员在线状态
     *
     * @param memberUserIds 团队成员用户ID列表
     * @return 团队成员在线状态信息映射
     */
    public Map<Long, Map<String, Object>> getTeamMembersOnlineStatus(List<Long> memberUserIds) {
        Map<Long, Map<String, Object>> result = new HashMap<>();

        if (memberUserIds == null || memberUserIds.isEmpty()) {
            return result;
        }

        logger.info("获取团队成员在线状态: memberCount={}", memberUserIds.size());

        try {
            // 批量获取在线状态
            Map<Long, Boolean> onlineStatusMap = batchCheckOnlineStatus(memberUserIds);

            // 批量获取最后活跃时间
            Map<Long, String> lastActiveTimeMap = batchGetLastActiveTime(memberUserIds);

            // 批量获取用户状态
            Map<Long, Map<String, Object>> userStatusMap = batchGetUserStatus(memberUserIds);

            // 组装结果
            for (Long userId : memberUserIds) {
                boolean isOnline = onlineStatusMap.getOrDefault(userId, false);
                String lastActiveTime = lastActiveTimeMap.get(userId);
                Map<String, Object> userStatus = userStatusMap.get(userId);

                Map<String, Object> statusInfo = new HashMap<>();
                statusInfo.put("userId", userId);
                statusInfo.put("isOnline", isOnline);
                statusInfo.put("lastActiveTime", lastActiveTime);
                statusInfo.put("status", isOnline ? "ONLINE" : "OFFLINE");

                if (userStatus != null) {
                    statusInfo.put("detailedStatus", userStatus.get("status"));
                    statusInfo.put("statusUpdateTime", userStatus.get("updateTime"));
                }

                result.put(userId, statusInfo);
            }

            long onlineCount = result.values().stream()
                .mapToLong(info -> (Boolean) info.get("isOnline") ? 1 : 0)
                .sum();

            logger.info("团队成员在线状态获取完成: totalMembers={}, onlineCount={}",
                memberUserIds.size(), onlineCount);

        } catch (Exception e) {
            logger.error("❌ 获取团队成员在线状态失败", e);
            // 如果批量获取失败，为每个用户设置默认状态
            for (Long userId : memberUserIds) {
                Map<String, Object> statusInfo = new HashMap<>();
                statusInfo.put("userId", userId);
                statusInfo.put("isOnline", false);
                statusInfo.put("status", "OFFLINE");
                result.put(userId, statusInfo);
            }
        }

        return result;
    }

    /**
     * 批量检查用户在线状态
     *
     * @param userIds 用户ID列表
     * @return 用户在线状态映射
     */
    public Map<Long, Boolean> batchCheckOnlineStatus(List<Long> userIds) {
        Map<Long, Boolean> result = new HashMap<>();

        if (userIds == null || userIds.isEmpty()) {
            return result;
        }

        try {
            // 先批量检查在线用户Hash
            List<Object> userIdStrings = userIds.stream()
                .map(String::valueOf)
                .collect(Collectors.toList());

            List<Object> hashResults = redisTemplate.opsForHash()
                .multiGet(ONLINE_USERS_KEY, userIdStrings);

            // 再批量检查心跳键是否存在
            List<String> heartbeatKeys = userIds.stream()
                .map(userId -> USER_HEARTBEAT_PREFIX + userId)
                .collect(Collectors.toList());

            List<Object> heartbeatResults = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
                for (String key : heartbeatKeys) {
                    connection.exists(key.getBytes());
                }
                return null;
            });

            for (int i = 0; i < userIds.size(); i++) {
                // 必须同时满足：在在线用户Hash中存在 AND 心跳键存在
                boolean inOnlineHash = i < hashResults.size() && hashResults.get(i) != null;
                boolean heartbeatExists = i < heartbeatResults.size() && Boolean.TRUE.equals(heartbeatResults.get(i));
                boolean isOnline = inOnlineHash && heartbeatExists;

                result.put(userIds.get(i), isOnline);
            }

        } catch (Exception e) {
            logger.error("❌ 批量检查用户在线状态失败", e);
            // 如果批量检查失败，逐个检查
            for (Long userId : userIds) {
                result.put(userId, isUserOnline(userId));
            }
        }

        return result;
    }

    /**
     * 批量获取用户最后活跃时间
     *
     * @param userIds 用户ID列表
     * @return 用户最后活跃时间映射
     */
    private Map<Long, String> batchGetLastActiveTime(List<Long> userIds) {
        Map<Long, String> result = new HashMap<>();

        try {
            // 从在线用户Hash中批量获取最后活跃时间
            List<Object> userIdStrings = userIds.stream()
                .map(String::valueOf)
                .collect(Collectors.toList());

            List<Object> lastActiveTimes = redisTemplate.opsForHash()
                .multiGet(ONLINE_USERS_KEY, userIdStrings);

            for (int i = 0; i < userIds.size(); i++) {
                Object lastActiveTime = i < lastActiveTimes.size() ? lastActiveTimes.get(i) : null;
                if (lastActiveTime != null) {
                    // 处理Redis返回的不同类型数据
                    String timeStr;
                    if (lastActiveTime instanceof Long) {
                        timeStr = String.valueOf(lastActiveTime);
                    } else if (lastActiveTime instanceof String) {
                        timeStr = (String) lastActiveTime;
                    } else {
                        timeStr = lastActiveTime.toString();
                    }
                    result.put(userIds.get(i), timeStr);
                }
            }

        } catch (Exception e) {
            logger.error("❌ 批量获取用户最后活跃时间失败", e);
        }

        return result;
    }

    /**
     * 批量获取用户状态
     *
     * @param userIds 用户ID列表
     * @return 用户状态信息映射
     */
    private Map<Long, Map<String, Object>> batchGetUserStatus(List<Long> userIds) {
        Map<Long, Map<String, Object>> result = new HashMap<>();

        try {
            for (Long userId : userIds) {
                Map<String, Object> userStatus = getUserStatus(userId);
                if (userStatus != null && !userStatus.isEmpty()) {
                    result.put(userId, userStatus);
                }
            }

        } catch (Exception e) {
            logger.error("❌ 批量获取用户状态失败", e);
        }

        return result;
    }


}
